package edu.spbsu.nonsm00th.model;

import edu.spbsu.nonsm00th.util.Precondition;
import java.util.Arrays;

/**
 * Point in space representation class.
 *
 * @author mbuzdov
 */
public class Point {

    /** Coordinate array. */
    private double x[];

    /** Dimension of point space. */
    private int dimension;

    /**
     * Constructor for <code>Point</code>.
     * Initializes <code>Point</code> coordinates with values of <code>x</code>.
     * 
     * @param x the values for initialization.
     */
    public Point(double... x) {
        this.dimension = x.length;
        this.x = x;
    }

    /**
     * Constructor for <code>Point</code>.
     * Initializes <code>Point</code> with all coordinates are 0.0.
     * 
     * @param N demension of <code>Point</code>.
     */
    public Point(int N) {
        this.dimension = N;
        this.x = new double[N];
        for (int i = 0; i < N; i++) {
            this.x[i] = 0.0;
        }
    }

    /**
     * Gets coordinate values as array.
     *
     * @return the array of coordinate values.
     */
    protected double[] getXs() {
        return x;
    }

    /**
     * Sets coordinate values.
     *
     * @param values the values to set.
     */
    protected void setXs(double[] values) {
        Precondition.notNull(values, "Array of values");
        Precondition.check(dimension == values.length, "Array of values must have size " + dimension);
        this.x = values;
    }

    /**
     * Gets x-coordinate.
     * @return the value of x-coordinate.
     */
    public double getX() {
        return getXi(0);
    }

    /**
     * Sets x-coordinate.
     * @param x the value to set.
     */
    public void setX(double x) {
        setXi(0, x);
    }

    /**
     * Gets y-coordinate.
     * @return the value of y-coordinate.
     */
    public double getY() {
        return getXi(1);
    }

    /**
     * Sets y-coordinate.
     * @param y the value to set.
     */
    public void setY(double y) {
        setXi(1, y);
    }

    /**
     * Gets z-coordinate.
     * @return the value of z-coordinate.
     */
    public double getZ() {
        return getXi(2);
    }

    /**
     * Sets z-coordinate.
     * @param z the value to set.
     */
    public void setZ(double z) {
        setXi(2, z);
    }

    /**
     * Gets value of <code>i</code>-th coordinate.
     *
     * @param i the index of coordinate.
     * @return the value of coordinate.
     */
    public double getXi(int i) {
        Precondition.check(i < dimension, "Index must be less than " + dimension);
        return x[i];
    }

    /**
     * Sets <code>i</code>-th coordinate.
     * 
     * @param i the index of coordinate.
     * @param value the value to set.
     */
    public void setXi(int i, double value) {
        Precondition.check(i < dimension, "Index must be less than " + dimension);
        this.x[i] = value;
    }

    /**
     * Gets the demension.
     *
     * @return the demension.
     */
    public int getDimension() {
        return dimension;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Point) {
            final Point to = (Point) obj;
            if (to.dimension != this.dimension) {
                return false;
            }
            for (int i = 0; i < to.dimension; i++) {
                if (to.x[i] != this.x[i]) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 47 * hash + Arrays.hashCode(this.x);
        hash = 47 * hash + this.dimension;
        return hash;
    }

}
